import tkinter as tk
from tkinter import ttk, messagebox
import threading
from selenium import webdriver
from selenium.webdriver.edge.options import Options
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import re
import os
from datetime import datetime
import subprocess
import time
import logging
import socket
import uiautomator2 as u2

# Настройка логирования
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# ------------------ Глобальные переменные ------------------ #
successful_attempts = 0
failed_attempts = 0
batch_success_count = 0
processing_thread = None
pause_event = threading.Event()
pause_event.set()
is_processing = False
error_attempt_counter = 0
auto_paused_due_to_wifi = False
log_autoscroll_enabled = True
log_autoscroll_var = None  # будет инициализирована ниже

# Пути к файлам
tokens_file = r"C:\Users\1\Desktop\VS 2022\Получение токенов ВК\tokens.txt"
failed_logins_file = r"C:\Users\1\Desktop\VS 2022\Получение токенов ВК\неуспешные логи.txt"
login_pass_file = r"C:\Users\1\Desktop\VS 2022\Получение токенов ВК\log pass.txt"
driver_path = r"C:\Users\1\Desktop\edgedriver_win64\msedgedriver.exe"
last_limit_file = r"last limit.txt"
hide_browser_state_file = r"hide_browser_state.txt"
last_model_file = r"last_model.txt"
run_scenario_state_file = r"run_scenario_state.txt"

devices_by_model = {}
selected_model_var = None
selected_wifi_var = None
available_wifi_networks = []

# ------------------ Функции для работы с лимитом ------------------ #
def get_limit_value():
    try:
        return int(limit_entry.get())
    except ValueError:
        return 0

def save_last_limit():
    try:
        with open(last_limit_file, "w", encoding="utf-8") as f:
            f.write(limit_entry.get())
    except Exception as e:
        log_message(f"Ошибка при сохранении лимита: {e}", log_text, msg_type="error")

def load_last_limit():
    try:
        with open(last_limit_file, "r", encoding="utf-8") as f:
            return f.read().strip()
    except Exception:
        return "0"

# ------------------ Новая функция разделителя токенов ------------------ #
def add_token_separator():
    try:
        with open(tokens_file, "a", encoding="utf-8") as f:
            now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            separator = f"\n{'='*30} {now} {'='*30}\n"
            f.write(separator)
    except Exception as e:
        log_message(f"Ошибка при добавлении разделителя в файл токенов: {e}", log_text, msg_type="error")

# ------------------ Функции сохранения состояния чекбоксов ------------------ #
def save_hide_browser_state(state: bool):
    try:
        with open(hide_browser_state_file, "w", encoding="utf-8") as f:
            f.write(str(state))
    except Exception as e:
        log_message(f"Ошибка при сохранении состояния чекбокса: {e}", log_text, msg_type="error")

def load_hide_browser_state() -> bool:
    try:
        with open(hide_browser_state_file, "r", encoding="utf-8") as f:
            return f.read().strip().lower() == "true"
    except Exception:
        return False

def hide_browser_state_changed(*args):
    save_hide_browser_state(hide_browser_var.get())

def save_run_scenario_state(state: bool):
    try:
        with open(run_scenario_state_file, "w", encoding="utf-8") as f:
            f.write(str(state))
    except Exception as e:
        log_message(f"Ошибка при сохранении состояния сценария: {e}", log_text, msg_type="error")

def load_run_scenario_state() -> bool:
    try:
        with open(run_scenario_state_file, "r", encoding="utf-8") as f:
            return f.read().strip().lower() == "true"
    except Exception:
        return False

def run_scenario_state_changed(*args):
    save_run_scenario_state(run_scenario_var.get())

def save_last_model(model: str):
    try:
        with open(last_model_file, "w", encoding="utf-8") as f:
            f.write(model)
    except Exception as e:
        log_message(f"Ошибка при сохранении выбранной модели: {e}", log_text, msg_type="error")

def load_last_model() -> str:
    try:
        with open(last_model_file, "r", encoding="utf-8") as f:
            return f.read().strip()
    except Exception:
        return ""

def on_model_selected(event=None):
    model = selected_model_var.get()
    save_last_model(model)

# ------------------ Подключение к сети и Wi-Fi ------------------ #
def is_connected() -> bool:
    try:
        socket.create_connection(("8.8.8.8", 53), timeout=2)
        return True
    except OSError:
        return False

def get_connected_wifi() -> str:
    try:
        output = subprocess.check_output("netsh wlan show interfaces", shell=True, encoding="utf-8")
        for line in output.splitlines():
            if "SSID" in line and "BSSID" not in line:
                parts = line.split(":", 1)
                if len(parts) > 1:
                    ssid = parts[1].strip()
                    return ssid if ssid else "Нет подключения"
        return "Нет подключения"
    except Exception as e:
        return f"Ошибка: {e}"

def get_available_networks() -> list:
    networks = []
    try:
        output = subprocess.check_output("netsh wlan show networks", shell=True, encoding="utf-8")
        for line in output.splitlines():
            if "SSID" in line and ":" in line:
                parts = line.split(":", 1)
                if len(parts) > 1:
                    ssid = parts[1].strip()
                    if ssid and ssid not in networks:
                        networks.append(ssid)
    except Exception as e:
        log_message(f"Ошибка при получении списка сетей: {e}", log_text, msg_type="error")
    return networks

# ------------------ Сценарии UIAutomator2 ------------------ #
def log_scenario_message(message):
    logging.info(message)

def lower_second_shade_and_toggle_switches(d: u2.Device):
    try:
        log_scenario_message("Опускаем шторку уведомлений...")
        d.open_quick_settings()
        time.sleep(0.5)
        log_scenario_message("Шторка опущена.")
        switch1 = d.xpath('//*[@resource-id="com.android.systemui:id/quick_tile_layout"]/android.widget.Switch[1]')
        if switch1.wait(timeout=5):
            switch1.click()
            log_scenario_message("Первый свитч нажат.")
            time.sleep(0.5)
            switch1.click()
            log_scenario_message("Первый свитч повторно нажат.")
        else:
            log_scenario_message("Первый свитч не найден.")
        time.sleep(0.5)
        switch2 = d.xpath('//*[@resource-id="com.android.systemui:id/quick_tile_layout"]/android.widget.Switch[2]')
        if switch2.wait(timeout=5):
            switch2.click()
            log_scenario_message("Второй свитч нажат.")
        else:
            log_scenario_message("Второй свитч не найден.")
        log_scenario_message("Поднимаем шторку свайпом...")
        d.swipe(500, 1700, 500, 500, duration=0.5)
        time.sleep(0.5)
        log_scenario_message("Шторка поднята.")
    except Exception as e:
        log_scenario_message(f"Ошибка при выполнении сценария: {e}")

def run_flight_mode_scenario():
    """Сценарий переключения режима полёта и точки доступа."""
    chosen_model = selected_model_var.get()
    if not chosen_model:
        log_message("Не выбрана модель смартфона для сценария. Сценарий не будет запущен.", log_text, msg_type="error")
        return
    serial = devices_by_model.get(chosen_model)
    if not serial:
        log_message(f"Не найден serial для модели '{chosen_model}'.", log_text, msg_type="error")
        return
    try:
        log_message(f"Запуск сценария (режим полёта, точка доступа) на устройстве '{chosen_model}'.",
                    log_text, msg_type="info")
        d = u2.connect(serial)
        log_message(f"Устройство подключено: {d.device_info}", log_text, msg_type="info")
        subprocess.run([
            "adb", "-s", serial, "shell", "am", "start", "-a", "android.settings.AIRPLANE_MODE_SETTINGS"
        ])
        time.sleep(0.5)
        switch_element = d(resourceId="android:id/switch_widget")
        switch_element.click_exists(timeout=5)
        time.sleep(0.5)
        switch_element.click_exists(timeout=5)
        time.sleep(1)
        d.app_start("com.android.settings", ".TetherSettings")
        time.sleep(0.5)
        if d(resourceId="com.android.settings:id/recycler_view").child(index=0).exists(timeout=2):
            d(resourceId="com.android.settings:id/recycler_view").child(index=0).click()
            time.sleep(0.5)
        for _ in range(3):
            d.press("back")
            time.sleep(0.3)
        log_message("Ожидание подключения к Wi-Fi на ПК...", log_text, msg_type="info")
        while not is_connected():
            time.sleep(0.5)
        log_message("Wi-Fi подключен на ПК. Продолжается обработка аккаунтов.", log_text, msg_type="info")
        time.sleep(3)
    except Exception as e:
        log_message(f"Ошибка при выполнении сценария смены режима: {e}", log_text, msg_type="error")

# ------------------ Логирование и обновление ------------------ #
def log_message(message, text_widget, msg_type="info"):
    """Добавляет сообщение в лог-гиджет через after для безопасности потоков."""
    def append_message():
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        full_message = f"[{timestamp}] {message}\n"
        text_widget.config(state=tk.NORMAL)
        if msg_type == "info":
            text_widget.insert(tk.END, full_message, "info")
        elif msg_type == "success":
            text_widget.insert(tk.END, full_message, "success")
        elif msg_type == "error":
            text_widget.insert(tk.END, full_message, "error")
        if text_widget is log_text and log_autoscroll_enabled:
            text_widget.see(tk.END)
        text_widget.config(state=tk.DISABLED)
    text_widget.after(0, append_message)

def append_token(token):
    successful_tokens_text.config(state=tk.NORMAL)
    successful_tokens_text.insert(tk.END, token + "\n")
    successful_tokens_text.config(state=tk.DISABLED)

def append_failed_login(login, password):
    failed_logins_text.config(state=tk.NORMAL)
    failed_logins_text.insert(tk.END, f"{login}:{password}\n")
    failed_logins_text.config(state=tk.DISABLED)

def update_status_labels():
    accounts = read_login_passwords()
    total_accounts = len(accounts)
    remaining = total_accounts - (successful_attempts + failed_attempts)
    success_label.config(text=f"Успешные попытки: {successful_attempts}")
    failure_label.config(text=f"Неуспешные попытки: {failed_attempts}")
    count_label.config(text=f"Всего аккаунтов: {total_accounts}")
    remaining_label.config(text=f"Оставшиеся аккаунты: {remaining}")

# ------------------ Работа с файлами ------------------ #
def save_token_to_file(token, is_error=False):
    try:
        with open(tokens_file, "a", encoding='utf-8') as file:
            if is_error:
                file.write(f"Ошибка: {token}\n")
            else:
                file.write(token + "\n")
        log_message(f"{'Ошибка' if is_error else 'Токен'} сохранён в файле: {tokens_file}", log_text, msg_type="info")
        if not is_error:
            append_token(token)
    except Exception as e:
        log_message(f"Ошибка при сохранении токена: {e}", log_text, msg_type="error")

def save_failed_login(login, password):
    try:
        with open(failed_logins_file, "a", encoding='utf-8') as file:
            file.write(f"{login}:{password}\n")
        log_message(f"Неуспешный логин сохранён в файле: {failed_logins_file}", log_text, msg_type="info")
        append_failed_login(login, password)
    except Exception as e:
        log_message(f"Ошибка при сохранении неуспешного логина: {e}", log_text, msg_type="error")

def read_login_passwords():
    if not os.path.exists(login_pass_file):
        log_message(f"Файл с логинами и паролями не найден: {login_pass_file}", log_text, msg_type="error")
        return []
    with open(login_pass_file, "r", encoding='utf-8') as file:
        accounts = [line.strip().split(":", 1) for line in file if line.strip()]
    return accounts

# ------------------ Основная логика через Selenium ------------------ #
def open_browser(login, password):
    global successful_attempts, failed_attempts, batch_success_count
    log_message(f"Открытие браузера для аккаунта: {login}", log_text, msg_type="info")
    options = Options()
    options.add_argument("--inprivate")
    
    if hide_browser_var.get():
        options.add_argument("--headless")
        options.add_argument("--disable-gpu")
        log_message("Браузер запускается в скрытом режиме (headless).", log_text, msg_type="info")
    
    service = Service(driver_path)
    driver = webdriver.Edge(service=service, options=options)
    driver.get("https://vkhost.github.io/")
    log_message("Страница https://vkhost.github.io/ открыта.", log_text, msg_type="info")
    
    try:
        button = WebDriverWait(driver, 15).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "button.btn[onclick='auth(6121396)']"))
        )
        button.click()
        log_message("Кнопка авторизации нажата.", log_text, msg_type="info")
        WebDriverWait(driver, 15).until(lambda d: len(d.window_handles) > 1)
        driver.switch_to.window(driver.window_handles[-1])
        log_message(f"Переключено на новую вкладку с URL: {driver.current_url}", log_text, msg_type="info")
        WebDriverWait(driver, 15).until(
            lambda d: d.current_url.startswith("https://oauth.vk.com/authorize?")
        )
        log_message(f"Страница авторизации ВКонтакте открыта: {driver.current_url}", log_text, msg_type="info")
        email_field = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "input[type='text'][name='email']"))
        )
        password_field = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "input[type='password'][name='pass']"))
        )
        email_field.send_keys(login)
        log_message("Логин введён.", log_text, msg_type="info")
        password_field.send_keys(password)
        log_message("Пароль введён.", log_text, msg_type="info")
        submit_button = WebDriverWait(driver, 15).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "button.flat_button.oauth_button.button_wide[type='submit']"))
        )
        submit_button.click()
        log_message("Кнопка 'Войти' нажата.", log_text, msg_type="info")
        
        try:
            found_error = WebDriverWait(driver, 5).until(
                EC.any_of(
                    EC.presence_of_element_located((By.CSS_SELECTOR, ".box_error")),
                    EC.presence_of_element_located((By.CSS_SELECTOR, ".oauth_captcha"))
                )
            )
            error_class = found_error.get_attribute("class")
            if "box_error" in error_class:
                error_text = "Невалидный логин или пароль."
            elif "oauth_captcha" in error_class:
                error_text = "Найдена капча"
            else:
                error_text = "Неизвестная ошибка"
            failed_attempts += 1
            # Добавляем номер (login) к тексту ошибки
            save_token_to_file(f"{error_text}: {login}", is_error=True)
            save_failed_login(login, password)
            log_message(f"Ошибка: {error_text} Авторизация неуспешна.", log_text, msg_type="error")
            append_token(f"{error_text}: {login}")
            return
        except TimeoutException:
            pass

        allow_button = WebDriverWait(driver, 15).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "button.flat_button[onclick='return allow(this);']"))
        )
        allow_button.click()
        log_message("Кнопка подтверждения нажата.", log_text, msg_type="info")
        WebDriverWait(driver, 15).until(
            lambda d: d.current_url.startswith("https://oauth.vk.com/blank.html#access_token=")
        )
        log_message(f"Страница с токеном загружена: {driver.current_url}", log_text, msg_type="info")
        url = driver.current_url
        token_match = re.search(r"access_token=([^&]+)&expires_in", url)
        if token_match:
            token = token_match.group(1)
            save_token_to_file(token)
            successful_attempts += 1
            batch_success_count += 1
            log_message("Токен успешно получен.", log_text, msg_type="success")
            current_limit = get_limit_value()
            if current_limit > 0 and batch_success_count >= current_limit:
                run_flight_mode_scenario()
                batch_success_count = 0
        else:
            failed_attempts += 1
            save_token_to_file(f"Токен не найден в URL: {login}", is_error=True)
            save_failed_login(login, password)
            log_message("Ошибка: Токен не найден в URL.", log_text, msg_type="error")
            append_token(f"Токен не найден в URL: {login}")
    except TimeoutException:
        failed_attempts += 1
        save_token_to_file(f"Превышено время ожидания элемента на странице: {login}", is_error=True)
        save_failed_login(login, password)
        log_message(f"Ошибка: Превышено время ожидания элемента на странице. Текущий URL: {driver.current_url}",
                    log_text, msg_type="error")
        append_token(f"Превышено время ожидания элемента на странице: {login}")
    except NoSuchElementException:
        failed_attempts += 1
        save_token_to_file(f"Элемент не найден на странице: {login}", is_error=True)
        save_failed_login(login, password)
        log_message("Ошибка: Элемент не найден на странице.", log_text, msg_type="error")
        append_token(f"Элемент не найден на странице: {login}")
    except Exception as e:
        failed_attempts += 1
        save_token_to_file(f"{str(e)}: {login}", is_error=True)
        save_failed_login(login, password)
        log_message(f"Неожиданная ошибка: {e}", log_text, msg_type="error")
        append_token(f"Неожиданная ошибка: {login}")
    finally:
        driver.quit()
        log_message("Браузер закрыт.", log_text, msg_type="info")
        update_status_labels()

def process_all_accounts():
    global is_processing
    accounts = read_login_passwords()
    for login, password in accounts:
        pause_event.wait()
        if not is_processing:
            break
        log_message(f"Обработка аккаунта: {login}", log_text, msg_type="info")
        open_browser(login, password)
    is_processing = False
    start_button.config(state=tk.NORMAL)
    pause_button.config(state=tk.DISABLED)
    resume_button.config(state=tk.DISABLED)
    log_message("Обработка всех аккаунтов завершена.", log_text, msg_type="info")

# ------------------ Управление потоком ------------------ #
def start_browser_thread():
    global is_processing, processing_thread, auto_paused_due_to_wifi
    if is_processing:
        messagebox.showinfo("Информация", "Процесс уже запущен.")
        return

    # Добавляем разделитель с датой и временем в файл tokens.txt
    add_token_separator()

    if selected_wifi_var.get() != get_connected_wifi():
        messagebox.showwarning("Внимание", "Текущая Wi-Fi сеть не совпадает с выбранной.\nЗапуск невозможен.")
        return

    save_last_limit()
    is_processing = True
    pause_event.set()
    auto_paused_due_to_wifi = False

    start_button.config(state=tk.DISABLED)
    pause_button.config(state=tk.NORMAL)
    resume_button.config(state=tk.DISABLED)

    def _run():
        try:
            if run_scenario_var.get():
                scenario_thread = threading.Thread(target=run_flight_mode_scenario, daemon=True)
                scenario_thread.start()
                scenario_thread.join()
            process_all_accounts()
        except Exception as ex:
            log_message(f"Ошибка в основном потоке: {ex}", log_text, msg_type="error")
        finally:
            start_button.config(state=tk.NORMAL)
            pause_button.config(state=tk.DISABLED)
            resume_button.config(state=tk.DISABLED)
    processing_thread = threading.Thread(target=_run, daemon=True)
    processing_thread.start()

def pause_processing():
    global auto_paused_due_to_wifi
    if not is_processing:
        return
    pause_event.clear()
    auto_paused_due_to_wifi = False
    log_message("Процесс приостановлен (вручную).", log_text, msg_type="info")
    pause_button.config(state=tk.DISABLED)
    resume_button.config(state=tk.NORMAL)

def resume_processing():
    if not is_processing:
        return
    if selected_wifi_var.get() != get_connected_wifi():
        messagebox.showwarning("Внимание", "Невозможно возобновить: текущая Wi-Fi сеть не совпадает с выбранной.")
        return
    pause_event.set()
    log_message("Процесс восстановлен (вручную).", log_text, msg_type="info")
    pause_button.config(state=tk.NORMAL)
    resume_button.config(state=tk.DISABLED)

# ------------------ Функции копирования ------------------ #
def copy_successful_tokens():
    content = successful_tokens_text.get("1.0", tk.END).strip()
    if not content:
        messagebox.showinfo("Информация", "Нет токенов для копирования.")
        return
    root.clipboard_clear()
    root.clipboard_append(content)
    log_message("Токены скопированы в буфер обмена.", log_text, msg_type="info")

def copy_failed_logins():
    content = failed_logins_text.get("1.0", tk.END).strip()
    if not content:
        messagebox.showinfo("Информация", "Нет неуспешных логинов для копирования.")
        return
    root.clipboard_clear()
    root.clipboard_append(content)
    log_message("Неуспешные логины и пароли скопированы в буфер обмена.", log_text, msg_type="info")

def copy_selection():
    try:
        selected_text = root.focus_get().get(tk.SEL_FIRST, tk.SEL_LAST)
        root.clipboard_clear()
        root.clipboard_append(selected_text)
    except tk.TclError:
        pass

def select_all(text_widget):
    text_widget.tag_add(tk.SEL, "1.0", tk.END)
    text_widget.mark_set(tk.INSERT, "1.0")
    text_widget.see(tk.INSERT)

# ------------------ Автопрокрутка ------------------ #
def toggle_log_autoscroll():
    global log_autoscroll_enabled
    log_autoscroll_enabled = log_autoscroll_var.get()

# ------------------ Контекстные меню ------------------ #
def show_log_context_menu(event):
    log_context_menu.tk_popup(event.x_root, event.y_root)

def show_text_context_menu(event):
    text_context_menu.tk_popup(event.x_root, event.y_root)

# ------------------ Сканирование ADB ------------------ #
def scan_devices():
    global devices_by_model
    devices_by_model.clear()
    try:
        result = subprocess.run(["adb", "devices", "-l"], capture_output=True, text=True, check=False)
        output = result.stdout.strip().splitlines()
        for line in output[1:]:
            line = line.strip()
            if not line or "offline" in line or "unauthorized" in line or "unknown" in line:
                continue
            parts = line.split()
            serial = parts[0]
            model = None
            for p in parts:
                if p.startswith("model:"):
                    model = p.split(":", 1)[1]
                    break
            if not model:
                model = "UnknownModel"
            devices_by_model[model] = serial
        log_message(f"Устройства обновлены: {devices_by_model}", log_text, msg_type="info")
    except Exception as e:
        log_message(f"Ошибка при сканировании устройств: {e}", log_text, msg_type="error")

    model_list = list(devices_by_model.keys())
    current_selection = load_last_model()

    def update_gui():
        device_combobox["values"] = model_list
        if current_selection in model_list:
            selected_model_var.set(current_selection)
        elif model_list:
            selected_model_var.set(model_list[0])
        else:
            selected_model_var.set("Нет подключённых устройств")
    root.after(0, update_gui)

def scan_devices_async():
    threading.Thread(target=scan_devices, daemon=True).start()

# ------------------ Обновление списка Wi‑Fi ------------------ #
def refresh_wifi_list():
    global available_wifi_networks
    available_wifi_networks = get_available_networks()
    current_ssid = get_connected_wifi()

    def update_wifi_combobox():
        wifi_combobox["values"] = available_wifi_networks
        if current_ssid in available_wifi_networks:
            selected_wifi_var.set(current_ssid)
    root.after(0, update_wifi_combobox)

def refresh_wifi_async():
    threading.Thread(target=refresh_wifi_list, daemon=True).start()

# ------------------ Окна для просмотра/очистки ------------------ #
def open_tokens_window():
    if hasattr(open_tokens_window, 'window') and open_tokens_window.window.winfo_exists():
        open_tokens_window.window.focus()
        return
    tokens_window = tk.Toplevel(root)
    tokens_window.title("Успешные токены")
    tokens_window.geometry("600x400")
    frame = ttk.Frame(tokens_window, padding="10")
    frame.pack(fill=tk.BOTH, expand=True)
    tokens_text = tk.Text(frame, height=20, width=70, state=tk.DISABLED, wrap=tk.WORD)
    tokens_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
    scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=tokens_text.yview)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
    tokens_text.config(yscrollcommand=scrollbar.set)
    
    def load_tokens():
        tokens_text.config(state=tk.NORMAL)
        tokens_text.delete("1.0", tk.END)
        if os.path.exists(tokens_file):
            with open(tokens_file, "r", encoding='utf-8') as file:
                tokens = file.read()
                tokens_text.insert(tk.END, tokens)
        else:
            tokens_text.insert(tk.END, "Файл с токенами не найден.")
        tokens_text.config(state=tk.DISABLED)
        
    def clear_tokens():
        if messagebox.askyesno("Подтверждение", "Вы уверены, что хотите очистить все токены?"):
            try:
                open(tokens_file, "w", encoding='utf-8').close()
                tokens_text.config(state=tk.NORMAL)
                tokens_text.delete("1.0", tk.END)
                tokens_text.config(state=tk.DISABLED)
                log_message("Все токены очищены.", log_text, msg_type="info")
                successful_tokens_text.config(state=tk.NORMAL)
                successful_tokens_text.delete("1.0", tk.END)
                successful_tokens_text.config(state=tk.DISABLED)
                update_status_labels()
            except Exception as e:
                messagebox.showerror("Ошибка", f"Не удалось очистить токены: {e}")
    
    load_tokens()
    button_frame = ttk.Frame(tokens_window)
    button_frame.pack(pady=5)
    clear_button = ttk.Button(button_frame, text="Очистить", command=clear_tokens)
    clear_button.pack()
    open_tokens_window.window = tokens_window

def open_failed_logins_window():
    if hasattr(open_failed_logins_window, 'window') and open_failed_logins_window.window.winfo_exists():
        open_failed_logins_window.window.focus()
        return
    failed_window = tk.Toplevel(root)
    failed_window.title("Неуспешные логины и пароли")
    failed_window.geometry("600x400")
    frame = ttk.Frame(failed_window, padding="10")
    frame.pack(fill=tk.BOTH, expand=True)
    failed_text = tk.Text(frame, height=20, width=70, state=tk.DISABLED, wrap=tk.WORD)
    failed_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
    scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=failed_text.yview)
    scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
    failed_text.config(yscrollcommand=scrollbar.set)
    
    def load_failed_logins():
        failed_text.config(state=tk.NORMAL)
        failed_text.delete("1.0", tk.END)
        if os.path.exists(failed_logins_file):
            with open(failed_logins_file, "r", encoding='utf-8') as file:
                failed_logins = file.read()
                failed_text.insert(tk.END, failed_logins)
        else:
            failed_text.insert(tk.END, "Файл с неуспешными логинами не найден.")
        failed_text.config(state=tk.DISABLED)
    
    def clear_failed_logins():
        if messagebox.askyesno("Подтверждение", "Вы уверены, что хотите очистить все неуспешные логины и пароли?"):
            try:
                open(failed_logins_file, "w", encoding='utf-8').close()
                failed_text.config(state=tk.NORMAL)
                failed_text.delete("1.0", tk.END)
                failed_text.config(state=tk.DISABLED)
                log_message("Все неуспешные логины и пароли очищены.", log_text, msg_type="info")
                failed_logins_text.config(state=tk.NORMAL)
                failed_logins_text.delete("1.0", tk.END)
                failed_logins_text.config(state=tk.DISABLED)
                update_status_labels()
            except Exception as e:
                messagebox.showerror("Ошибка", f"Не удалось очистить неуспешные логины: {e}")
    
    load_failed_logins()
    button_frame = ttk.Frame(failed_window)
    button_frame.pack(pady=5)
    clear_button = ttk.Button(button_frame, text="Очистить", command=clear_failed_logins)
    clear_button.pack()
    open_failed_logins_window.window = failed_window

def clear_all_logs():
    if messagebox.askyesno("Подтверждение", "Вы уверены, что хотите очистить все журналы, токены и неуспешные логины?"):
        try:
            open(tokens_file, "w", encoding='utf-8').close()
            open(failed_logins_file, "w", encoding='utf-8').close()
            log_text.config(state=tk.NORMAL)
            log_text.delete("1.0", tk.END)
            log_text.config(state=tk.DISABLED)
            successful_tokens_text.config(state=tk.NORMAL)
            successful_tokens_text.delete("1.0", tk.END)
            successful_tokens_text.config(state=tk.DISABLED)
            failed_logins_text.config(state=tk.NORMAL)
            failed_logins_text.delete("1.0", tk.END)
            failed_logins_text.config(state=tk.DISABLED)
            global successful_attempts, failed_attempts
            successful_attempts = 0
            failed_attempts = 0
            update_status_labels()
            log_message("Все журналы и данные очищены.", log_text, msg_type="info")
            if hasattr(open_tokens_window, 'window') and open_tokens_window.window.winfo_exists():
                open_tokens_window.window.destroy()
            if hasattr(open_failed_logins_window, 'window') and open_failed_logins_window.window.winfo_exists():
                open_failed_logins_window.window.destroy()
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось очистить журналы: {e}")

def restart_failed_accounts():
    global error_attempt_counter, successful_attempts, failed_attempts, batch_success_count
    successful_attempts = 0
    failed_attempts = 0
    batch_success_count = 0
    error_attempt_counter = 0
    update_status_labels()
    
    successful_tokens_text.config(state=tk.NORMAL)
    successful_tokens_text.delete("1.0", tk.END)
    successful_tokens_text.config(state=tk.DISABLED)
    
    content = failed_logins_text.get("1.0", tk.END).strip()
    if not content:
        messagebox.showinfo("Информация", "Нет аккаунтов для перезапуска.")
        return

    failed_logins_text.config(state=tk.NORMAL)
    failed_logins_text.delete("1.0", tk.END)
    failed_logins_text.config(state=tk.DISABLED)
    try:
        with open(failed_logins_file, "w", encoding="utf-8") as f:
            f.write("")
    except Exception as e:
        log_message(f"Ошибка при очистке файла неуспешных логинов: {e}", log_text, msg_type="error")
    
    def process_failed():
        lines = content.splitlines()
        for line in lines:
            parts = line.split(":", 1)
            if len(parts) == 2:
                login, password = parts
                log_message(f"Перезапуск аккаунта: {login}", log_text, msg_type="info")
                open_browser(login, password)
        update_status_labels()
    
    threading.Thread(target=process_failed, daemon=True).start()

# ------------------ Авто-проверка Wi‑Fi ------------------ #
def update_wifi_status():
    global auto_paused_due_to_wifi
    current_wifi = get_connected_wifi()
    wifi_status_label.config(text=f"Подключенный Wi-Fi: {current_wifi}")
    selected_wifi = selected_wifi_var.get()
    
    if selected_wifi and selected_wifi != current_wifi:
        if is_processing and pause_event.is_set():
            pause_event.clear()
            auto_paused_due_to_wifi = True
            log_message("Процесс приостановлен автоматически (Wi-Fi mismatch).", log_text, msg_type="info")
        if not is_processing:
            start_button.config(state=tk.DISABLED)
    else:
        if not is_processing:
            start_button.config(state=tk.NORMAL)
        else:
            if auto_paused_due_to_wifi and not pause_event.is_set():
                pause_event.set()
                auto_paused_due_to_wifi = False
                log_message("Процесс возобновлён автоматически (Wi-Fi соответствует).", log_text, msg_type="info")
                time.sleep(0.5)
    root.after(1000, update_wifi_status)

def on_wifi_selected(event=None):
    global auto_paused_due_to_wifi
    selected = selected_wifi_var.get()
    current_wifi = get_connected_wifi()
    if selected and selected != current_wifi:
        if is_processing and pause_event.is_set():
            pause_event.clear()
            auto_paused_due_to_wifi = True
            log_message("Процесс приостановлен автоматически (Wi-Fi mismatch).", log_text, msg_type="info")
        if not is_processing:
            start_button.config(state=tk.DISABLED)
    else:
        if not is_processing:
            start_button.config(state=tk.NORMAL)
        else:
            if auto_paused_due_to_wifi and not pause_event.is_set():
                pause_event.set()
                auto_paused_due_to_wifi = False
                log_message("Процесс возобновлён автоматически (Wi-Fi выбран совпадает с текущей).", log_text, msg_type="info")
                time.sleep(0.5)

# ------------------ Интерфейс Tkinter ------------------ #
root = tk.Tk()
root.title("Получение токенов ВК")
root.resizable(True, True)
root.geometry("1150x750")

main_frame = ttk.Frame(root, padding="10")
main_frame.pack(fill=tk.BOTH, expand=True)

# ------- Панель управления -------
control_section = ttk.LabelFrame(main_frame, text="Панель управления", padding="5")
control_section.pack(fill=tk.X, pady=5)

control_row1 = ttk.Frame(control_section)
control_row1.pack(fill=tk.X, padx=5, pady=2)

start_button = ttk.Button(control_row1, text="Запустить", command=start_browser_thread)
start_button.pack(side=tk.LEFT, padx=5)

pause_button = ttk.Button(control_row1, text="Приостановить", command=pause_processing, state=tk.DISABLED)
pause_button.pack(side=tk.LEFT, padx=5)

resume_button = ttk.Button(control_row1, text="Восстановить", command=resume_processing, state=tk.DISABLED)
resume_button.pack(side=tk.LEFT, padx=5)

restart_button = ttk.Button(control_row1, text="Перезапустить ошибки", command=restart_failed_accounts)
restart_button.pack(side=tk.LEFT, padx=5)

control_row2 = ttk.Frame(control_section)
control_row2.pack(fill=tk.X, padx=5, pady=2)

hide_browser_var = tk.BooleanVar()
hide_browser_var.set(load_hide_browser_state())
hide_browser_var.trace_add("write", hide_browser_state_changed)
hide_browser_check = ttk.Checkbutton(control_row2, text="Скрыть браузер", variable=hide_browser_var)
hide_browser_check.pack(side=tk.LEFT, padx=5)

run_scenario_var = tk.BooleanVar()
run_scenario_var.set(load_run_scenario_state())
run_scenario_var.trace_add("write", run_scenario_state_changed)
run_scenario_check = ttk.Checkbutton(control_row2, text="Запустить сценарий", variable=run_scenario_var)
run_scenario_check.pack(side=tk.LEFT, padx=5)

update_labels_button = ttk.Button(control_row2, text="Обновить метки", command=update_status_labels)
update_labels_button.pack(side=tk.LEFT, padx=5)

clear_logs_button = ttk.Button(control_row2, text="Очистить все журналы", command=clear_all_logs)
clear_logs_button.pack(side=tk.LEFT, padx=5)

scan_devices_button = ttk.Button(control_row2, text="Сканировать устройства", command=scan_devices_async)
scan_devices_button.pack(side=tk.LEFT, padx=5)

control_row3 = ttk.Frame(control_section)
control_row3.pack(fill=tk.X, padx=5, pady=2)

selected_model_var = tk.StringVar()
device_combobox = ttk.Combobox(control_row3, textvariable=selected_model_var, state="readonly", width=30)
device_combobox.pack(side=tk.LEFT, padx=5)
device_combobox.set("Нет подключённых устройств")
device_combobox.bind("<<ComboboxSelected>>", on_model_selected)

limit_label = ttk.Label(control_row3, text="Лимит токенов:")
limit_label.pack(side=tk.LEFT, padx=5)
limit_entry = ttk.Entry(control_row3, width=5)
limit_entry.pack(side=tk.LEFT)
limit_entry.bind("<FocusOut>", lambda e: save_last_limit())
limit_entry.insert(0, load_last_limit())

control_row4 = ttk.Frame(control_section)
control_row4.pack(fill=tk.X, padx=5, pady=2)

wifi_label = ttk.Label(control_row4, text="Выбрать Wi-Fi:")
wifi_label.pack(side=tk.LEFT, padx=5)

selected_wifi_var = tk.StringVar()
wifi_combobox = ttk.Combobox(control_row4, textvariable=selected_wifi_var, state="readonly", width=30)
wifi_combobox.pack(side=tk.LEFT, padx=5)
wifi_combobox.bind("<<ComboboxSelected>>", on_wifi_selected)

refresh_wifi_button = ttk.Button(control_row4, text="Обновить Wi-Fi", command=refresh_wifi_async)
refresh_wifi_button.pack(side=tk.LEFT, padx=5)

# ------- Статус -------
status_section = ttk.LabelFrame(main_frame, text="Статус", padding="5")
status_section.pack(fill=tk.X, pady=5)

status_frame = ttk.Frame(status_section)
status_frame.pack(fill=tk.X, padx=5, pady=5)

success_label = ttk.Label(status_frame, text="Успешные попытки: 0", foreground="green", cursor="hand2")
success_label.pack(side=tk.LEFT, padx=10)
success_label.bind("<Button-1>", lambda e: copy_successful_tokens())

failure_label = ttk.Label(status_frame, text="Неуспешные попытки: 0", foreground="red", cursor="hand2")
failure_label.pack(side=tk.LEFT, padx=10)
failure_label.bind("<Button-1>", lambda e: copy_failed_logins())

count_label = ttk.Label(status_frame, text="Всего аккаунтов: 0", foreground="blue")
count_label.pack(side=tk.LEFT, padx=10)

remaining_label = ttk.Label(status_frame, text="Оставшиеся аккаунты: 0", foreground="orange")
remaining_label.pack(side=tk.LEFT, padx=10)

wifi_status_label = ttk.Label(status_frame, text="Подключенный Wi-Fi: Неизвестно", foreground="purple")
wifi_status_label.pack(side=tk.LEFT, padx=10)

# ------- Основная область (лог, токены и неуспешные) -------
logs_frame = ttk.Frame(main_frame)
logs_frame.pack(fill=tk.BOTH, expand=True, pady=10)

log_frame = ttk.LabelFrame(logs_frame, text="Журнал действий")
log_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
log_text = tk.Text(log_frame, height=20, width=40, state=tk.DISABLED, wrap=tk.WORD)
log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
log_scrollbar = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=log_text.yview)
log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
log_text.config(yscrollcommand=log_scrollbar.set)
log_text.tag_config("info", foreground="black")
log_text.tag_config("success", foreground="green")
log_text.tag_config("error", foreground="red")

tokens_frame = ttk.LabelFrame(logs_frame, text="Успешные токены")
tokens_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5)
successful_tokens_text = tk.Text(tokens_frame, height=20, width=40, state=tk.DISABLED, wrap=tk.WORD)
successful_tokens_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
tokens_scrollbar = ttk.Scrollbar(tokens_frame, orient=tk.VERTICAL, command=successful_tokens_text.yview)
tokens_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
successful_tokens_text.config(yscrollcommand=tokens_scrollbar.set)
successful_tokens_text.tag_config("success", foreground="green")
successful_tokens_text.tag_config("error", foreground="red")

failed_frame = ttk.LabelFrame(main_frame, text="Неуспешные логины и пароли")
failed_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=10)
failed_logins_text = tk.Text(failed_frame, height=10, width=80, state=tk.DISABLED, wrap=tk.WORD)
failed_logins_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
failed_scrollbar = ttk.Scrollbar(failed_frame, orient=tk.VERTICAL, command=failed_logins_text.yview)
failed_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
failed_logins_text.config(yscrollcommand=failed_scrollbar.set)
failed_logins_text.tag_config("error", foreground="red")

# ------- Контекстные меню -------
log_context_menu = tk.Menu(root, tearoff=0)
log_autoscroll_var = tk.BooleanVar(value=True)
log_context_menu.add_command(label="Копировать", command=copy_selection)
log_context_menu.add_command(label="Выбрать всё", command=lambda: select_all(log_text))
log_context_menu.add_separator()
log_context_menu.add_checkbutton(
    label="Автопрокрутка",
    variable=log_autoscroll_var,
    onvalue=True,
    offvalue=False,
    command=toggle_log_autoscroll
)

text_context_menu = tk.Menu(root, tearoff=0)
text_context_menu.add_command(label="Копировать", command=copy_selection)
text_context_menu.add_command(label="Выбрать всё", command=lambda: select_all(root.focus_get()))

log_text.bind("<Button-3>", show_log_context_menu)
successful_tokens_text.bind("<Button-3>", show_text_context_menu)
failed_logins_text.bind("<Button-3>", show_text_context_menu)

# ------- Инициализация -------
log_message("Журнал действий:", log_text, msg_type="info")
update_status_labels()
scan_devices_async()   # Сканируем устройства в отдельном потоке
refresh_wifi_async()   # Обновляем список Wi‑Fi в отдельном потоке
update_wifi_status()   # Запускаем цикл проверки Wi‑Fi каждые 1000 мс

root.mainloop()
